#include "config.h"

#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>


#define DBG_SUBSYS S_LIBINTERFACE

#include "ynet_net.h"
#include "../sock/sock_tcp.h"
#include "../net/net_events.h"
#include "net_global.h"
#include "sunrpc_passive.h"
#include "sunrpc_proto.h"
#include "dbg.h"

static int __pasv_sd__ = -1;

static int __sunrpc_accept__()
{
        int ret;
        net_proto_t proto;
        net_handle_t nh;

        DINFO("new conn for sd %d\n", __pasv_sd__);

        _memset(&proto, 0x0, sizeof(net_proto_t));

        proto.head_len = sizeof(sunrpc_request_t);
        proto.reader = net_events_handle_read;
        proto.writer = net_events_handle_write;
        proto.pack_len = sunrpc_pack_len;
        proto.pack_handler = sunrpc_pack_handler;

        ret = sdevent_accept(__pasv_sd__, &nh, &proto,
                             YNET_RPC_BLOCK);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = tcp_sock_tuning(nh.u.sd.sd, 1, YNET_RPC_NONBLOCK);
        if (unlikely(ret))
                GOTO(err_fd, ret);

        ret = sdevent_add(&nh, Y_EPOLL_EVENTS, NULL, NULL);
        if (unlikely(ret))
                GOTO(err_fd, ret);

        return 0;
err_fd:
        close(nh.u.sd.sd);
err_ret:
        return ret;
}

static void *__sunrpc_accept(void *_arg)
{
        int ret;

        (void) _arg;
        DINFO("start...\n");

        while (1) {
                ret = sock_poll_sd(__pasv_sd__, 1000 * 1000, POLLIN);
                if (unlikely(ret)) {
                        if (ret == ETIMEDOUT || ret == ETIME)
                                continue;
                        else
                                GOTO(err_ret, ret);
                 }

                ret = __sunrpc_accept__();
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        }

        return NULL;
err_ret:
        UNIMPLEMENTED(__DUMP__);
        return NULL;
}



int sunrpc_tcp_passive(const char *port)
{
        int ret;
        pthread_t th;
        pthread_attr_t ta;

        YASSERT(__pasv_sd__ == -1);

        ret = tcp_sock_hostlisten(&__pasv_sd__, NULL, port,
                                  YNET_QLEN, YNET_RPC_BLOCK, 1);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        (void) pthread_attr_init(&ta);
        (void) pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED);
 
        ret = pthread_create(&th, &ta, __sunrpc_accept, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        DINFO("listen %s\n", port);

        return 0;
err_ret:
        return ret;
}
